﻿using System;
using System.IO;
using System.Text;

namespace xxpl1.Utils
{
    public class XorEncryptor
    {
        private byte key;

        public XorEncryptor()
        {
            var keyRandom = new Random();
            key = (byte)keyRandom.Next(256);
        }

        public byte Key
        {
            get { return key; }
            set { key = value; }
        }

        public void Encrypt(byte[] value)
        {
            Encrypt(value, 0, value.Length);
        }

        public void Encrypt(byte[] value, int offset, int len)
        {
            for (int i = offset; i < offset + len; i++)
            {
                value[i] ^= key;
            }
        }
    }

    public class BlockInputStream : IDisposable
    {
        private readonly XorEncryptor ep = new XorEncryptor();
        private readonly Stream innerStream;
        private bool keySpecify;
        private int length;

        public int Length
        {
            get { return length; }
        }

        public BlockInputStream(Stream innerStream)
        {
            this.innerStream = innerStream;
        }

        public void WriteBlock(string value)
        {
            byte[] block = Encoding.ASCII.GetBytes(value);
            WriteBlock(block);
        }

        public void WriteBlock(byte[] block)
        {
            if (block == null || block.Length == 0)
            {
                return;
            }
            Write(BitConverter.GetBytes(block.Length), 0, 4);
            Write(block, 0, block.Length);
            length += block.Length + 4;
        }

        public void Write(byte[] array, int offset, int count)
        {
            if (!keySpecify)
            {
                keySpecify = true;
                innerStream.WriteByte(ep.Key);
            }

            ep.Encrypt(array, offset, count);

            innerStream.Write(array, offset, count);
        }

        public string ReadString()
        {
            byte[] value = ReadByteArray();

            return value != null ? Encoding.ASCII.GetString(value) : string.Empty;
        }

        public byte[] ReadByteArray()
        {
            int size = ReadInt();

            var value = new byte[size];
            int result = Read(value, 0, size);

            if (result == size)
            {
                return value;
            }

            if (result == 0)
                return null;

            throw new IOException("Read Error");
        }

        public int ReadInt()
        {
            var x = new byte[4];

            int result = Read(x, 0, 4);

            if (result == 4)
            {
                return BitConverter.ToInt32(x, 0);
            }

            if (result == 0)
                return 0;

            throw new IOException("Read Error");
        }

        public int Read(byte[] array, int offset, int count)
        {
            if (!keySpecify)
            {
                keySpecify = true;
                innerStream.ReadByte();
                ep.Key = (byte)innerStream.ReadByte();
            }

            int result = innerStream.Read(array, offset, count);

            ep.Encrypt(array, offset, result);

            return result;
        }

        public void Close()
        {
            innerStream.Flush();
            innerStream.Close();
        }

        public void Dispose()
        {
            innerStream.Dispose();
        }
    }
}